#pragma once
#include "VulkanDevice.h"
namespace Alice{
    class VulkanQueue;
    class CGIVulkanCommandBufferPool;
    class VulkanCommandListContext;
    class VulkanSemaphore;
    class VulkanFence;
    class VulkanLayoutManager;
    class CGIVulkanCommandBufferManager;
    class VulkanCommandBuffer{
    public:
#ifdef _DEBUG
        std::string mDebugName;
#endif
        VulkanDevice* mDevice;
        VkCommandBuffer mCommandBuffer;
        enum class EState : uint8
        {
            ReadyForBegin,
            IsInsideBegin,
            IsInsideRenderPass,
            HasEnded,
            Submitted,
            NotAllocated,
            NeedReset,
        };
        EState State;
        uint8 bNeedsDynamicStateSet			: 1;
        uint8 bHasPipeline					: 1;
        uint8 bHasViewport					: 1;
        uint8 bHasScissor					: 1;
        uint8 bHasStencilRef				: 1;
        uint8 bIsUploadOnly					: 1;
        uint8 bIsUniformBufferBarrierAdded	: 1;
        CGIVulkanCommandBufferPool* mCommandBufferPool;
        VulkanLayoutManager* mLayoutManager;
        std::vector<VkViewport> mCurrentViewports;
        std::vector<VkRect2D> mCurrentScissors;
        VulkanFence* mFence;
        std::vector<VkPipelineStageFlags> mWaitFlags;
        std::vector<VulkanSemaphore*> mWaitSemaphores;
	    std::vector<VulkanSemaphore*> mSubmittedWaitSemaphores;
        VulkanCommandBuffer(
#ifdef _DEBUG
            const char* inDebugName,
#endif
            VulkanDevice* InDevice, CGIVulkanCommandBufferPool* InCommandBufferPool, bool bInIsUploadOnly);
        inline VkCommandBuffer GetHandle() const
        {
            return mCommandBuffer;
        }
        inline VulkanLayoutManager& GetLayoutManager() const
        {
            return *mLayoutManager;
        }
        inline bool IsInsideRenderPass() const
        {
            return State == EState::IsInsideRenderPass;
        }

        inline bool IsOutsideRenderPass() const
        {
            return State == EState::IsInsideBegin;
        }

        inline bool HasBegun() const
        {
            return State == EState::IsInsideBegin || State == EState::IsInsideRenderPass;
        }

        inline bool HasEnded() const
        {
            return State == EState::HasEnded;
        }

        inline bool IsSubmitted() const
        {
            return State == EState::Submitted;
        }
        void AllocMemory();
        void RefreshFenceStatus();
        void Begin();
        void End();
        void InitializeTimings(VulkanCommandListContext* InContext);
        void MarkSemaphoresAsSubmitted()
        {
            mWaitFlags.clear();
            // Move to pending delete list
            mSubmittedWaitSemaphores = mWaitSemaphores;
            mWaitSemaphores.clear();
        }
        void AddWaitSemaphore(VkPipelineStageFlags inWaitFlags, VulkanSemaphore* inWaitSemaphore);
        void AddWaitSemaphores(VkPipelineStageFlags inWaitFlags, std::vector<VulkanSemaphore*> inWaitSemaphores);
        CGIVulkanCommandBufferPool* GetOwner() const
        {
            return mCommandBufferPool;
        }
    };
    class CGIVulkanCommandBufferPool
    {
    public:
        VkCommandPool mHandle;
        VulkanDevice* mDevice;
        std::vector<VulkanCommandBuffer*> mCmdBuffers;
        std::vector<VulkanCommandBuffer*> mFreeCmdBuffers;
        CGIVulkanCommandBufferManager& mMgr;
        CGIVulkanCommandBufferPool(VulkanDevice* InDevice, CGIVulkanCommandBufferManager& InMgr);
        VulkanCommandBuffer* Create(
#ifdef _DEBUG
            const char* inDebugName,
#endif
            bool bIsUploadOnly);
        void Create(
#ifdef _DEBUG
            const char* inDebugName,
#endif
            uint32 QueueFamilyIndex);
        inline VkCommandPool GetHandle() const
        {
            return mHandle;
        }
        void RefreshFenceStatus(VulkanCommandBuffer* SkipCmdBuffer = nullptr);
        inline CGIVulkanCommandBufferManager& GetMgr() const
        {
            return mMgr;
        }
    };
    class CGIVulkanCommandBufferManager
    {
    public:
        VulkanDevice* mDevice;
        VulkanCommandListContext* mContext;
        CGIVulkanCommandBufferPool mPool;
        VulkanQueue* mQueue;
        VulkanCommandBuffer* mActiveCmdBuffer;
        VulkanCommandBuffer* mUploadCmdBuffer;
        VulkanSemaphore* mUploadCmdBufferSemaphore;
        CGIVulkanCommandBufferManager(VulkanDevice* InDevice, VulkanCommandListContext* InContext);
        ~CGIVulkanCommandBufferManager();
        void Init(VulkanCommandListContext* InContext);
        inline VulkanCommandListContext* GetCommandListContext() const 
        {
            return mContext;
        }
        
        inline VulkanQueue* GetQueue() const
        {
            return mQueue;
        }
        VulkanCommandBuffer* GetUploadCmdBuffer();
        void SubmitUploadCmdBuffer(uint32 NumSignalSemaphores = 0, VkSemaphore* SignalSemaphores = nullptr);
    };
}